﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Imaris;
using System.IO;
using System.Threading;

namespace ChiaraImages
{


  public delegate void DelProcessDone(bool done, int step);


  public class ActionThreadedParameters
  {
    public Func<bool> ProcessAction {get; set;}
    public DelProcessDone ProcessDone { get; set; }
    public int Step { get; set; }
  }


  public class ImarisProcess
  {
    public delegate void DelShowInformation(string info);
    private ApplicationClass m_apImaris;
    private string m_error;
    string m_tempFolder;
    string sVersionString = "100824";
    IInvoker m_invoker;
      
    #region Constructor
    public ImarisProcess(IInvoker invoker)
    {
      m_invoker = invoker;
    }
    #endregion

    #region Properties
    public DelShowInformation ShowInformation { private get; set; }
    #endregion

    public bool InitializationImaris()
    {
      try
      {
        ShowInformationAsyn("Initialization Imaris");
        m_apImaris = new ApplicationClass();
      }
      catch
      {
        m_error = "Impossible to Connect with Imaris!";
        return false;
      }
      return true;
    }

    public bool QuitImaris()
    {
      try
      {
        ShowInformationAsyn("Exit Imaris");
        m_apImaris.Quit();
        m_apImaris = null;
      }
      catch
      {
        m_error = "Impossible to Quit!";
        return false;
      }
      return true;
    }

    /// <summary>
    /// InitializationImarisAsyn nextStep will be 1
    /// </summary>
    /// <param name="ProcessDone"></param>
    public void InitializationImarisAsyn(DelProcessDone ProcessDone)
    {
      InitAndStartThreadedMethod(new ActionThreadedParameters { ProcessAction = () => InitializationImaris(), ProcessDone = ProcessDone, Step = 1 });
    }

    public void QuitImarisAsyn(DelProcessDone ProcessDone, int nextStep)
    {
      InitAndStartThreadedMethod(new ActionThreadedParameters { ProcessAction = () => QuitImaris(), ProcessDone = ProcessDone, Step = nextStep });
    }

    public void ConvertAllFilesAsyn(string[] fileNames, bool crop, DelProcessDone ProcessDone, int nextStep)
    {
      InitAndStartThreadedMethod(new ActionThreadedParameters { ProcessAction = () => ConvertAllFiles(fileNames, crop), ProcessDone = ProcessDone, Step = nextStep });
    }

    private bool ConvertAllFiles(string[] fileNames, bool crop)
    {
      var cote = '"'.ToString();
      m_tempFolder = Path.GetTempPath() + "ImarisProcess3";

      // clean up temps folder
      try
      {
        if (!Directory.Exists(m_tempFolder))
        {
          Directory.CreateDirectory(m_tempFolder);
        }
      }
      catch
      {
        m_error = "Impossible to work with the temps directory. Check if the temp folder is accessible!";
        QuitImaris();
        return false;
      }
      try
      {
        Directory.GetFiles(m_tempFolder).ToList().ForEach(fileTodelete => File.Delete(fileTodelete));
      }
      catch { }

      try
      {
        Directory.GetDirectories(m_tempFolder).ToList().ForEach(dirTodelete => Directory.Delete(dirTodelete, true));
      }
      catch { }
 

      if (!Directory.Exists(Path.GetDirectoryName(fileNames[0])))
      {
        m_error = "Process Path:" + Path.GetDirectoryName(fileNames[0]) + " does not exist!";
        QuitImaris();
        return false;
      }

      // copy all .tif in the temp path
      foreach (string orgFilePath in fileNames)
      {
        if (Path.GetExtension(orgFilePath).ToLower() == ".tif")
        {
          string fileNameToProcess = Path.GetFileName(orgFilePath);
          string subtempPath = Path.Combine(m_tempFolder, "p"+Guid.NewGuid().ToString());
          string processFile = Path.Combine(subtempPath, fileNameToProcess);

          try
          {
            if (!Directory.Exists(subtempPath))
            {
              Directory.CreateDirectory(subtempPath);
            }
            File.Copy(orgFilePath, processFile);
          }
          catch
          {
            m_error = "Impossible to work in temps file or impossible to copy the file in the Process Path. Check if all files (.tif) are accessible!";
            return false;
          }
          ShowInformationAsyn("Converted file: " + Path.GetFileName(processFile));
          try
          {
            m_apImaris.FileOpen(processFile,"");
          }
          catch (Exception e)
          {
            m_error = "Imaris Exception " + e.ToString() + " when opening the file :" + fileNameToProcess;
            return false;
          }

          if (m_apImaris.GetCurrentFileName() != processFile)
          {
            m_error = "Imaris Can not open the file : " + fileNameToProcess;
            return false;
          }
          //Load Data (Channels)
          var dsChannels = m_apImaris.mDataSet;
          var struChannels = dsChannels; // ? the same??

          //Set the voxel size to 0.178*0.178*0.2 um^3
          dsChannels.mExtendMaxX = 0.178F * dsChannels.mSizeX;
          dsChannels.mExtendMaxY = 0.178F * dsChannels.mSizeY;
          dsChannels.mExtendMaxZ = 0.200F * dsChannels.mSizeZ;
          dsChannels.mUnit = "um";

          //Set the channel name and colour

          dsChannels.SetChannelName(0, Path.GetFileNameWithoutExtension(fileNameToProcess));
          dsChannels.SetChannelColor(0, 1, 0, 0, 0);
          //}
          //if (colorChannel == EColorChannel.GFP)
          //{
          //  dsChannels.SetChannelName(0, "GFP");
          //  dsChannels.SetChannelColor(0, 0, 1, 0, 0);
          //}
          //Set last slice to zero of first channel (just in case
          //there is an error)
          //Set last slice to zero
          for (int iChannel = 0; iChannel < struChannels.mSizeC - 1; iChannel++)
          {
            var data = new UInt16[struChannels.mSizeX * struChannels.mSizeY];
            dsChannels.SetDataSlice(data, (uint)(struChannels.mExtendMaxZ - 1), (uint)iChannel, 0);
          }
          if (crop)
          {
            //Do a crop to region of interest
            var iStartX = (UInt16)((struChannels.mSizeX - 450) / 2);
            var iStartY = (UInt16)((struChannels.mSizeY - 450) / 2);
            dsChannels.Crop(iStartX, 450, iStartY, 450, 0, struChannels.mSizeZ, 0, struChannels.mSizeC, 0, struChannels.mSizeT);
          }


          //Set channel colour range to standard for channel 0
          dsChannels.SetChannelRange(0, 0, 65023);

          //Center view
          m_apImaris.mSurpassCamera.Fit();

          try
          {

            //Save data
            m_apImaris.FileSave(Path.Combine(Path.GetDirectoryName(orgFilePath), Path.GetFileNameWithoutExtension(fileNameToProcess)) + "tif_to_ims" + sVersionString + ".ims", "writer = " + cote + "Imaris5" + cote);
          }
          catch (Exception e)
          {
            m_error = "Imaris Exception " + e.ToString() + " when saving the file :" + Path.Combine(Path.GetDirectoryName(orgFilePath), Path.GetFileNameWithoutExtension(fileNameToProcess)) + "tif_to_ims" + sVersionString + ".ims";
            return false;
          }
        }
      }
      return true;
    }

    public bool MergeImarisFiles(string file1, string file2)
    {
      var bSliceDifference = false;
      var cote = '"'.ToString();

      try
      {
        m_apImaris.FileOpen(file1, "");
      }
      catch (Exception e)
      {
        m_error = "Imaris Exception " + e.ToString() + " when opening the file :" + file1;
        return false;
      }

      var dsMcherry = m_apImaris.mDataSet.Clone();

      try
      {
        m_apImaris.FileOpen(file2, "");
      }
      catch (Exception e)
      {
        m_error = "Imaris Exception " + e.ToString() + " when opening the file :" + file2;
        return false;
      }
      var dsGfp = m_apImaris.mDataSet;
      var iSmallerZ = dsGfp.mSizeZ;

      if (Math.Abs(iSmallerZ - dsMcherry.mSizeZ) > 2)
      {
        bSliceDifference = true;
      }
      if (iSmallerZ > dsMcherry.mSizeZ)
      {
        iSmallerZ = dsMcherry.mSizeZ;
      }
      dsMcherry.Resize(0, dsMcherry.mSizeX, 0, dsMcherry.mSizeY, 0, iSmallerZ, 0, dsMcherry.mSizeC + 1, 0, dsMcherry.mSizeT);
      dsMcherry.SetChannelColor(dsMcherry.mSizeC - 1, 0, 1, 0, 0);
      dsMcherry.SetChannelName(dsMcherry.mSizeC - 1, Path.GetFileNameWithoutExtension(file2));
      dsMcherry.SetChannelRange(dsMcherry.mSizeC - 1, 0, 65023);
      //Copy channels
      for (uint iZSlice = 0; iZSlice < iSmallerZ - 1; iZSlice++)
      {
        dsMcherry.SetDataSlice(dsGfp.GetDataSlice(iZSlice, 0, 0), iZSlice, dsMcherry.mSizeC - 1, 0);
      }
      m_apImaris.mDataSet = dsMcherry;

      try
      {
        //Save data
        m_apImaris.FileSave(Path.Combine(Path.GetDirectoryName(file1), Path.GetFileNameWithoutExtension(file1)) + "GAndRtif_to_ims" + sVersionString + ".ims", "writer = " + cote + "Imaris5" + cote);
      }
      catch (Exception e)
      {
        m_error = "Imaris Exception " + e.ToString() + " when saving the file :" + Path.Combine(Path.GetDirectoryName(file1), Path.GetFileNameWithoutExtension(file1)) + "GAndRtif_to_ims" + sVersionString + ".ims";
        return false;
      }
      return true;
    }

    #region Tools

    public string GetError()
    {
      return m_error;
    }

    private void ShowInformationAsyn(string info)
    {
      if (ShowInformation != null)
      {
        m_invoker.BeginInvoke(ShowInformation, info);
      }
    }

    private void InitAndStartThreadedMethod(ActionThreadedParameters actionThreadedParameters)
    {
      var thread = new Thread((ParameterizedThreadStart)StartThreadedMethod);
      thread.Start(actionThreadedParameters);
    }

    private void StartThreadedMethod(object obj)
    {
      // show Wait Box
      m_invoker.BeginInvoke((Action)System.Windows.Forms.Application.DoEvents);
      Thread.Sleep(1000);
      var actionThreadedParameters = (ActionThreadedParameters)obj;
      var result = actionThreadedParameters.ProcessAction();

      if (actionThreadedParameters.ProcessDone != null)
      {
        m_invoker.BeginInvoke(actionThreadedParameters.ProcessDone, new object[]{result,actionThreadedParameters.Step});
      }
    }

    #endregion

  }
}
